﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using WinScriptEngine.Parses.Interface;
using System.Globalization;
using WinScriptEngine.Parses.Scope;

namespace WinScriptEngine.Parses.Impl
{
    #region ==== 异常 ====
    class NumberParseException : UndefinedException
    {
        public NumberParseException(string message) : base(message) { }
        public NumberParseException(string message, Exception inner) : base(message, inner) { }
        protected NumberParseException(SerializationInfo info, StreamingContext context) : base(info, context) { }
    }
    class NumberRuntimeException : RuntimeException
    {
        public NumberRuntimeException(string message) : base(message) { }
        public NumberRuntimeException(string message, Exception inner) : base(message, inner) { }
        protected NumberRuntimeException(SerializationInfo info, StreamingContext context) : base(info, context) { }
    }
    #endregion

    /// <summary>
    /// 数字解析类
    /// </summary>
    class NumberParseImpl : SingletonObject<NumberParseImpl>, IParse
    {
        delegate Metadata ParseCallback(List<char> upper, IEnumerator<char> position, NumberAttribute attribute = NumberAttribute.Dec);
        delegate bool CheckCallback(char chr);

        #region ==== IParse属性实现 ====
        public string VTypeName => "Number";
        #endregion

        /// <summary>
        /// 数字类型
        /// </summary>
        enum NumberAttribute
        {
            Dub = 0,
            Bin = 2,
            Oct = 8,
            Dec = 10,
            Hex = 16,
        }

        /// <summary>
        /// 不同的数字类型，不同的取值范围
        /// </summary>
        private static Dictionary<NumberAttribute, CheckCallback> checkCallbacks = new()
        {
            { NumberAttribute.Dub, char.IsDigit },
            { NumberAttribute.Hex, delegate (char chr) { return chr >= '0' && chr <= '9'
                                                            || (chr >= 'a' && chr <= 'z') 
                                                            || (chr >= 'A' && chr <= 'Z'); } },
            { NumberAttribute.Oct, delegate (char chr) { return chr >= '0' && chr <= '7'; } }, 
            { NumberAttribute.Dec, char.IsDigit },
            { NumberAttribute.Bin, delegate (char chr) { return chr >= '0' && chr <= '1'; } }
        };

        #region ==== IParse接口实现 ====

        public bool Invoke(InvokeEnvironment vCpu)
        {
            if (vCpu.needNumber < 1)
                throw new NumberRuntimeException("此处应为运算符");
            Metadata value = vCpu.instructions[vCpu.eip];
            vCpu.results.Push(value);
            --vCpu.needNumber;
            return true;
        }
        public bool TryParse(ref Metadata upper, char curChar)
        {
            return char.IsDigit(curChar) || curChar == '.';
        }

        public Metadata Parse(ref Metadata upper, IEnumerator<char> position)
        {
            if (!this.TryParse(ref upper, position.Current))
                throw new NumberParseException("Is not a number");

            NumberAttribute attribute = NumberAttribute.Dec;
            char prefix = position.Current;
            StringBuilder upperString = new();
            upperString.Append(prefix);

            if (prefix == '0')
            {
                if (!position.MoveNext())
                    return new Metadata(MetadataType.Value, 0);

                char nextChar = position.Current;
                upperString.Append(nextChar);
                switch (nextChar)
                {
                    case '.':
                        attribute = NumberAttribute.Dub;
                        break;
                    case 'x':
                    case 'X':
                        attribute = NumberAttribute.Hex;
                        break;
                    case 'b':
                    case 'B':
                        attribute = NumberAttribute.Bin;
                        break;
                    default:
                        if (char.IsDigit(nextChar))
                            attribute = NumberAttribute.Oct;
                        else
                            return new Metadata(MetadataType.Value, 0);
                        break;
                }
            }else if (prefix == '.')
                attribute = NumberAttribute.Dub;

            return ParseInt(upperString, position, attribute);
        }
        #endregion

        #region ==== 各种数字解析 ====
        /// <summary>
        /// 解析整数
        /// </summary>
        /// <param name="upper">上流字符</param>
        /// <param name="position">当前字符流</param>
        /// <param name="attribute">以什么格式解析</param>
        /// <returns>数值</returns>
        private static Metadata ParseInt(StringBuilder upper, IEnumerator<char> position, NumberAttribute attribute = NumberAttribute.Dec)
        {
            CheckCallback checkCallback;
            if (!checkCallbacks.TryGetValue(attribute, out checkCallback))
                throw new NumberParseException("Internal error, analytic function not found");

            if (attribute == NumberAttribute.Hex || attribute == NumberAttribute.Bin)
                upper.Clear();
            if (attribute == NumberAttribute.Dub)
                return ParseDouble(upper, position);

            while (position.MoveNext())
            {
                if (!checkCallback(position.Current))
                {
                    if (!(position.Current == '.' && attribute == NumberAttribute.Dec))
                        break;

                    upper.Append(position.Current);
                    return ParseDouble(upper, position);
                }
                upper.Append(position.Current);
            }
            string value = upper.ToString();
            return new Metadata(MetadataType.Value, Convert.ToInt32(value, (int)attribute));
        }
        /// <summary>
        /// 解析小数
        /// </summary>
        /// <param name="upper">上流字符</param>
        /// <param name="position">当前字符流</param>
        /// <param name="reserve">未使用，保留</param>
        /// <returns>数值</returns>
        private static Metadata ParseDouble(StringBuilder upper, IEnumerator<char> position, NumberAttribute reserve = NumberAttribute.Dec)
        {
            //
            bool haveOne = false;
            CheckCallback checkCallback;
            if(!checkCallbacks.TryGetValue(NumberAttribute.Dub, out checkCallback))
                throw new NumberParseException("Internal error, analytic function not found");
            
            while (position.MoveNext())
            {
                if (!checkCallback(position.Current))
                    break;
                haveOne = true;
                upper.Append(position.Current);
            }
            if (!haveOne)
                throw new NumberParseException("Is not valid double");
            string value = upper.ToString();
            return new Metadata(MetadataType.Value, Double.Parse(value));
        }
        #endregion
    }
}
